﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using ValidationFramework.Extensions;
namespace ValidationFramework.Reflection
{
    internal static class GenericTypeFactory
    {
        //internal static TInterface FillInstance<TInterface>(object genericInstance, Dictionary<string, object> propertyValueDictionary)
        //{
        //    Guard.ArgumentNotNull(propertyValueDictionary, "propertyValueDictionary");
        //    // Guard: generic instance must implement TInterface
        //    if (!typeof(TInterface).IsAssignableFrom(genericInstance.GetType()))
        //    {
        //        var genericTypeName = genericInstance.GetType().Name;
        //        var interfaceTypeName = typeof(TInterface).Name;
        //        var messageFormat = "GenericInstance of type '{0}' does not implement '{1}'. We can't fill the instance based off the interface";
        //        var message = messageFormat.Fill(genericTypeName, interfaceTypeName);
        //        throw new ArgumentOutOfRangeException(message);
        //    }

        //    // Set properties using interface
        //    var flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
        //    var properties = typeof(TInterface).GetProperties(flags);
        //    foreach (var keyValue in propertyValueDictionary)
        //    {
        //        var property = properties.Where(x => x.Name == keyValue.Key).SingleOrDefault();

        //        if (property != null)
        //        {
        //            property.SetValue(genericInstance, keyValue.Value, null);
        //        }
        //    }

        //    return (TInterface)genericInstance;
        //}


        internal static object CreateInstance(Type genericType, Type typeParam, params object[] constructorParams)
        {
            Guard.ArgumentNotNull(genericType, "genericType");
            Guard.ArgumentNotNull(typeParam, "typeParam");

            if (constructorParams == null)
                constructorParams = new object[0];

            Type[] typeArgs = { typeParam };
            var constructedRequiredRuleType = genericType.MakeGenericType(typeArgs);

            var paramTypes = constructorParams.Select(x => x.GetType()).ToArray<Type>();

            var constructorInfo = constructedRequiredRuleType.GetConstructor(paramTypes);

            if (constructorInfo == null)
            {
                var paramTypeNames = paramTypes.Select(x => x.Name).ToArray<string>();
                throw new NullReferenceException("Could not find constructor for generic type '{0}'. Tried to find constructor with the following params:'{1}'".Fill(genericType.Name, paramTypeNames.ToString()));
            }

            return constructorInfo.Invoke(constructorParams);
        }
    }
}
